package team.bluepen.supermarket.util;

import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 日志实用类
 * @author Kai
 */
@SuppressWarnings({"unused", "SameParameterValue"})
public final class Log {
    /** 输出流 */
    private static final PrintStream OUT_STREAM = System.out;

    private static final String DATE_PATTERN = "yyyy-MM-dd HH-mm-ss.SSS";
    private static final String COLOR_RED = "\033[31m";
    private static final String COLOR_YELLOW = "\033[33m";
    private static final String COLOR_BLUE = "\033[34m";
    private static final String COLOR_DEFAULT = "\033[0m";


    private Log() { }

    /**
     * 输出调试信息
     * @param tag 标签
     * @param msg 输出信息
     */
    public static void d(String tag, String msg) {
        d(tag, msg, true);
    }

    private static void refresh() {
        OUT_STREAM.flush();
        OUT_STREAM.print(COLOR_DEFAULT);
    }

    /**
     * 输出调试信息 <br> 请使用 {@link #d(String, String)}
     * @param msg 输出信息
     */
    public static void d(String msg) {
        String date = "[DEBUG][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]";
        OUT_STREAM.println(COLOR_DEFAULT + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace()) + "\n" + msg);
        refresh();
    }

    private static void d(String tag, String msg, boolean state) {
        String date = "[DEBUG][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_DEFAULT + date + " method: " // 使用new Throwable()获取堆栈信息开销较小
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state) + "\n" + msg);
        refresh();
    }

    /**
     * 输出提示信息
     * @param tag 标签
     * @param msg 输出输出信息
     */
    public static void i(String tag, String msg) {
        i(tag, msg, true);
    }

    /**
     * 输出提示信息 <br> 请使用 {@link #i(String, String)}
     * @param msg 输出信息
     */
    public static void i(String msg) {
        String date = "[INFO ][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]";
        OUT_STREAM.println(COLOR_BLUE + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace()) + "\n" + msg);
        refresh();
    }

    private static void i(String tag, String msg, boolean state) {
        String date = "[INFO ][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_BLUE + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state) + "\n" + msg);
        refresh();
    }

    /**
     * 输出警告信息
     * @param tag 标签
     * @param msg 输出信息
     */
    public static void w(String tag, String msg) {
        w(tag, msg, true);
    }

    /**
     * 输出警告信息（不含标签）<br> 推荐使用 {@link #w(String, String)}}
     * @param msg 信息
     */
    public static void w(String msg) {
        String date = "[WARN ][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]";
        OUT_STREAM.println(COLOR_YELLOW + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace()) + "\n" + msg);
        refresh();
    }

    private static void w(String tag, String msg, boolean state) {
        String date = "[WARN ][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_YELLOW + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), true) + "\n" + msg);
        refresh();
    }

    /**
     * 输出错误信息
     * @param tag 标签
     * @param throwable {@code Throwable} 对象
     * @param msg 输出信息
     */
    public static void e(String tag, String msg, Throwable throwable) {
        e(tag, msg, throwable, true);
    }

    public static void e(String tag, String msg) {
        e(tag, msg, true);
    }

    public static void e(String tag, Throwable throwable) {
        e(tag, throwable, true);
    }

    public static void e(Throwable throwable) {
       e(throwable, true);
    }

    public static void e(String msg) {
        String date = "[ERROR][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]";
        OUT_STREAM.println(COLOR_RED + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace()) + "\n" + msg);
        refresh();
    }

    private static void e(String tag, String msg, Throwable throwable, boolean state) {
        String date = "[ERROR][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_RED + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state) + "\n" + msg);
        if (throwable != null){
            throwable.printStackTrace(OUT_STREAM);
        }
        refresh();
    }

    private static void e(String tag, String msg, boolean state) {
        String date = "[ERROR][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_RED + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state) + "\n" + msg);
        refresh();
    }

    private static void e(String tag, Throwable throwable, boolean state) {
        String date = "[ERROR][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]" + "["+ tag +"]";
        OUT_STREAM.println(COLOR_RED + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state));
        if (throwable != null){
            throwable.printStackTrace(OUT_STREAM);
        }
        refresh();
    }

    private static void e(Throwable throwable, boolean state) {
        String date = "[ERROR][" + new SimpleDateFormat(DATE_PATTERN)
                .format(new Date()) + "]";
        OUT_STREAM.println(COLOR_RED + date + " method: "
                + getMethodFromStackTrace(new Throwable().getStackTrace(), state));
        if (throwable != null){
            throwable.printStackTrace(OUT_STREAM);
        }
        refresh();
    }

    /**
     * 获取当前调用方法（不可在{@code Servlet}等中使用）
     * @param elements {@code StackTrace} 数组
     * @return 调用方法
     */
    private static String formatStackTrace(StackTraceElement[] elements) {
        if (elements == null || elements.length == 0){
            return "Cannot get current method.";
        }
        return elements[elements.length - 1].toString();
    }

    /**
     * 获取当前调用方法（仅在{@code Servlet}中使用）
     * @param elements {@code StackTrace} 数组
     * @return 调用方法
     */
    private static String getMethodFromStackTrace(StackTraceElement[] elements) {
        return getMethodFromStackTrace(elements, false);
    }
    /**
     * 获取当前调用方法（仅在{@code Servlet}中使用）
     * @param elements {@code StackTrace} 数组
     * @param state 是否又经过了一次调用
     * @return 调用方法
     */
    private static String getMethodFromStackTrace(StackTraceElement[] elements, boolean state) {
        if (elements == null || elements.length < 2){
            return "Cannot get current method.";
        }
        if (!state){
            return elements[1].toString();
        }
        if (elements.length < 3){
            return "Cannot get current method.";
        }
        return elements[2].toString();
    }

    /**
     * 从堆栈中获取完整列表
     * @param elements {@code StackTrace} 数组
     * @return 含有完整列表的字符串
     */
    private static String formatStackTraceList(StackTraceElement[] elements) {
        if (elements == null || elements.length == 0){
            return "Cannot get current method list.";
        }
        List<String> stringList = new ArrayList<>();
        for (StackTraceElement e : elements){
            stringList.add(e.toString());
        }
        StringBuilder result = new StringBuilder();
        int i = 0;
        for (String a : stringList){
            if (i == stringList.size() - 1){
                result.append(a);
            } else {
                result.append(a).append(",\n\t");
            }
            i++;
        }
        return result.toString();
    }

    public static void d(String tag, int msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, long msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, boolean msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, float msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, double msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, char[] msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void d(String tag, Object msg){
        d(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, int msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, long msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, boolean msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, float msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, double msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, char[] msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void i(String tag, Object msg){
        i(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, int msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, long msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, boolean msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, float msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, double msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, char[] msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void w(String tag, Object msg){
        w(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, int msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, long msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, boolean msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, float msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, double msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, char[] msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, Object msg){
        e(tag, String.valueOf(msg), true);
    }

    public static void e(String tag, int msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, long msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, boolean msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, float msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, double msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, char[] msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }

    public static void e(String tag, Object msg, Throwable throwable){
        e(tag, String.valueOf(msg), throwable, true);
    }
}
